home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 2324 / 2324.xpi / chrome / sessionmanager.jar / content / sessionmanager / sessionbrowser.js < prev    next >
Encoding:
JavaScript  |  2009-10-10  |  14.6 KB  |  416 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is the nsSessionStore component.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Simon B├╝nzli <zeniko@gmail.com>
  18.  * Portions created by the Initial Developer are Copyright (C) 2008
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  * Morac <morac99-firefox@yahoo.com> - Modified for use with Session Manager
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const Cc = Components.classes;
  39. const Ci = Components.interfaces;
  40.  
  41. var gStateObject;
  42. var gTreeData;
  43. var gNoTabsChecked = false;
  44. var gAllTabsChecked = true;
  45. var gDeleting = false;
  46.  
  47. function initTreeView(aFileName, aDeleting) {
  48.   // Save deleting parameter
  49.   gDeleting = aDeleting;
  50.  
  51.   // Initialize tree data to default state
  52.   gNoTabsChecked = false;
  53.   gAllTabsChecked = true;
  54.   treeView.initialize();
  55.   
  56.   var state = null;
  57.  
  58.   // if chose crashed session read from sessionstore.js instead of session file
  59.   if (aFileName == "*") {
  60.     try {
  61.       var file = gSessionManager.getProfileFile("sessionstore.js");
  62.       // If file does not exist, try looking for SeaMonkey's sessionstore file
  63.       if (!file.exists()) {
  64.         file = gSessionManager.getProfileFile("sessionstore.json");
  65.       }
  66.       if (file.exists()) {
  67.         state = gSessionManager.readFile(file);
  68.       }
  69.     }
  70.     catch(ex) {}
  71.     if (!state)
  72.     {
  73.       gSessionManager.ioError();
  74.       return;
  75.     }
  76.   }
  77.   else {
  78.     state = gSessionManager.readSessionFile(gSessionManager.getSessionDir(aFileName));
  79.     if (!state)
  80.     {
  81.       gSessionManager.ioError();
  82.       return;
  83.     }
  84.  
  85.     if (!gSessionManager.mSessionRegExp.test(state))
  86.     {
  87.       gSessionManager.sessionError();
  88.       return;
  89.     }
  90.     state = state.split("\n")[4];
  91.   }
  92.  
  93.   var tabTree = document.getElementById("tabTree");
  94.   var winLabel = tabTree.getAttribute("_window_label");
  95.  
  96.   // Decrypt first, then evaluate
  97.   state = gSessionManager.decrypt(state);
  98.   if (!state) return;
  99.   gStateObject = gSessionManager.JSON_decode(state);
  100.   if (!gStateObject) return;
  101.   
  102.   gStateObject.windows.forEach(function(aWinData, aIx) {
  103.     var winState = {
  104.       label: winLabel.replace("%S", (aIx + 1)),
  105.       open: true,
  106.       checked: true,
  107.       ix: aIx
  108.     };
  109.     winState.tabs = aWinData.tabs.map(function(aTabData) {
  110.       var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
  111.       var iconURL = aTabData.attributes && aTabData.attributes.image || null;
  112.       // if no iconURL, look in pre Firefox 3.1 storage location
  113.       if (!iconURL && aTabData.xultab) {
  114.         iconURL = /image=(\S*)(\s)?/i.exec(aTabData.xultab);
  115.         if (iconURL) iconURL = iconURL[1];
  116.       }
  117.       // Trying to display a favicon for an https with an invalid certificate will throw up an exception box, so don't do that
  118.       // Firefox's about:sessionrestore also fails with authentication requests, but Session Manager seems okay with that so just
  119.       // use the work around for https.
  120.       if (/^https:/.test(iconURL))
  121.         iconURL = "moz-anno:favicon:" + iconURL;
  122.       return {
  123.         label: entry.title || entry.url,
  124.         url: entry.url,
  125.         checked: true,
  126.         src: iconURL,
  127.         parent: winState
  128.       };
  129.     });
  130.     gTreeData.push(winState);
  131.     for each (var tab in winState.tabs)
  132.       gTreeData.push(tab);
  133.   }, this);
  134.   
  135.   gNoTabsChecked = false;
  136.   gAllTabsChecked = true;  
  137.   
  138.   tabTree.view = treeView;
  139.   //tabTree.view.selection.select(0);
  140. }
  141.  
  142. // User actions
  143.  
  144. function storeSession() {
  145.   // remove all unselected tabs from the state before restoring it
  146.   // remove all selected tabs from state when deleting
  147.   var ix = gStateObject.windows.length - 1;
  148.   for (var t = gTreeData.length - 1; t >= 0; t--) {
  149.     if (treeView.isContainer(t)) {
  150.       if (gTreeData[t].checked === 0)
  151.         // this window will be restored or deleted partially
  152.         gStateObject.windows[ix].tabs = (gDeleting) ?
  153.           gStateObject.windows[ix].tabs.filter(function(aTabData, aIx) !gTreeData[t].tabs[aIx].checked) :
  154.           gStateObject.windows[ix].tabs.filter(function(aTabData, aIx) gTreeData[t].tabs[aIx].checked);
  155.       else if (!gTreeData[t].checked && !gDeleting)
  156.         // this window won't be restored at all
  157.         gStateObject.windows.splice(ix, 1);
  158.       else if (gTreeData[t].checked && gDeleting)
  159.         // this window will be deleted
  160.         gStateObject.windows.splice(ix, 1);
  161.       ix--;
  162.     }
  163.   }
  164.   var stateString = gSessionManager.JSON_encode(gStateObject);
  165.   
  166.   var smHelper = Cc["@morac/sessionmanager-helper;1"].getService(Ci.nsISessionManangerHelperComponent);
  167.   smHelper.setSessionData(gSessionManager.JSON_encode(gStateObject));
  168. }
  169.  
  170. function onTabTreeClick(aEvent) {
  171.   // don't react to right-clicks
  172.   if (aEvent.button == 2)
  173.     return;
  174.   
  175.   var row = {}, col = {};
  176.   treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
  177.   if (col.value) {
  178.     // restore this specific tab in the same window for middle-clicking
  179.     // or Ctrl+clicking or Meta+clicking on a tab's title
  180.     if (!gDeleting && (aEvent.button == 1 || aEvent.ctrlKey || aEvent.metaKey) && ((col.value.id == "title") || (col.value.id == "location"))) {
  181.       if (treeView.isContainer(row.value))
  182.         restoreSingleWindow(row.value);
  183.       else
  184.         restoreSingleTab(row.value, aEvent.shiftKey);
  185.     }
  186.     else if (col.value.id == "restore")
  187.       toggleRowChecked(row.value);
  188.   }
  189. }
  190.  
  191. function onTabTreeKeyDown(aEvent) {
  192.   switch (aEvent.keyCode)
  193.   {
  194.   case KeyEvent.DOM_VK_SPACE:
  195.     toggleRowChecked(document.getElementById("tabTree").currentIndex);
  196.     break;
  197.   case KeyEvent.DOM_VK_RETURN:
  198.     var ix = document.getElementById("tabTree").currentIndex;
  199.     if (aEvent.ctrlKey) {
  200.       if (treeView.isContainer(ix))
  201.         restoreSingleWindow(ix);
  202.       else
  203.         restoreSingleTab(ix, aEvent.shiftKey);
  204.     }
  205.     break;
  206.   case KeyEvent.DOM_VK_UP:
  207.   case KeyEvent.DOM_VK_DOWN:
  208.   case KeyEvent.DOM_VK_PAGE_UP:
  209.   case KeyEvent.DOM_VK_PAGE_DOWN:
  210.   case KeyEvent.DOM_VK_HOME:
  211.   case KeyEvent.DOM_VK_END:
  212.     aEvent.preventDefault(); // else the page scrolls unwantedly
  213.     break;
  214.   }
  215. }
  216.  
  217. // Helper functions
  218.  
  219. function getBrowserWindow() {
  220.   if (window.opener) {
  221.     return window.opener.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
  222.                         .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
  223.                         .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
  224.   }
  225.   else return null;
  226. }
  227.  
  228. function toggleRowChecked(aIx) {
  229.   var item = gTreeData[aIx];
  230.   item.checked = !item.checked;
  231.   treeView.treeBox.invalidateRow(aIx);
  232.   
  233.   function isChecked(aItem) aItem.checked;
  234.   
  235.   if (treeView.isContainer(aIx)) {
  236.     // (un)check all tabs of this window as well
  237.     for each (var tab in item.tabs) {
  238.       tab.checked = item.checked;
  239.       treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
  240.     }
  241.   }
  242.   else {
  243.     // update the window's checkmark as well (0 means "partially checked")
  244.     item.parent.checked = item.parent.tabs.every(isChecked) ? true :
  245.                           item.parent.tabs.some(isChecked) ? 0 : false;
  246.     treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
  247.   }
  248.   
  249.   gAllTabsChecked = gTreeData.every(isChecked);
  250.   gAcceptButton.disabled = gNoTabsChecked = !gTreeData.some(isChecked);
  251. }
  252.  
  253. function tabTreeSelect(aType) {
  254.  
  255.   function isChecked(aItem) { return aItem.checked; }
  256.  
  257.   for each (var item in gTreeData) {
  258.     // only act on window items
  259.     if (item.tabs) {
  260.       // if toggling and 0 ("partially checked") remain 0, otherwise toggle.  If not toggling just set/clear.
  261.       var check = (aType == "TOGGLE") ? ((item.checked === 0) ? 0 : !item.checked) : (aType == "ALL");
  262.       item.checked = check;
  263.       for each (var tab in item.tabs) {
  264.         tab.checked = (aType == "TOGGLE") ? !tab.checked : check;
  265.       }
  266.     }
  267.   }
  268.   gAllTabsChecked = gTreeData.every(isChecked);
  269.   gAcceptButton.disabled = gNoTabsChecked = !gTreeData.some(isChecked);
  270.   
  271.   // update the whole tree view
  272.   treeView.treeBox.invalidate();
  273. }
  274.  
  275. function restoreSingleWindow(aIx) {
  276.   // only allow this is there is an existing window open.  Basically if it's not a prompt at browser startup.
  277.   var win = getBrowserWindow();
  278.   if (!win) return;
  279.  
  280.   // gSingleWindowMode is set if Tab Mix Plus's single window mode is enabled
  281.   var TMP_SingleWindowMode = typeof(win.gSingleWindowMode) != "undefined" && win.gSingleWindowMode;
  282.  
  283.   var item = gTreeData[aIx];
  284.   var winState = { windows : new Array(1) };
  285.   winState.windows[0] = gStateObject.windows[item.ix];
  286.   
  287.   // if Tab Mix Plus's single window mode is enabled and there is an existing window restores all tabs in that window
  288.   gSessionManager.restoreSession(TMP_SingleWindowMode && win, gSessionManager.JSON_encode(winState), !TMP_SingleWindowMode, 
  289.                                  gSessionManager.mPref_save_closed_tabs < 2, false, TMP_SingleWindowMode, true);
  290.                                  
  291.   // bring current window back into focus
  292.   setTimeout(function() { window.focus(); }, 1000);
  293. }
  294.  
  295. function restoreSingleTab(aIx, aShifted) {
  296.   var win = getBrowserWindow();
  297.   if (!win) return;
  298.   var tabbrowser = win.gBrowser;
  299.   var newTab = tabbrowser.addTab();
  300.   var item = gTreeData[aIx];
  301.   
  302.   var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  303.   var tabState = gStateObject.windows[item.parent.ix]
  304.                              .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
  305.   ss.setTabState(newTab, gSessionManager.JSON_encode(tabState));
  306.   
  307.   // respect the preference as to whether to select the tab (the Shift key inverses)
  308.   var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  309.   if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
  310.     tabbrowser.selectedTab = newTab;
  311. }
  312.  
  313. // Tree controller
  314.  
  315. var treeView = {
  316.   _atoms: {},
  317.   _getAtom: function(aName)
  318.   {
  319.     if (!this._atoms[aName]) {
  320.       var as = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
  321.       this._atoms[aName] = as.getAtom(aName);
  322.     }
  323.     return this._atoms[aName];
  324.   },
  325.  
  326.   treeBox: null,
  327.   selection: null,
  328.  
  329.   get rowCount()                     { return gTreeData.length; },
  330.   setTree: function(treeBox)         { this.treeBox = treeBox; },
  331.   getCellText: function(idx, column) { 
  332.     if (column.id == "location") {
  333.       return gTreeData[idx].url ? gTreeData[idx].url : "";
  334.     }
  335.     else return gTreeData[idx].label; 
  336.   },
  337.   isContainer: function(idx)         { return "open" in gTreeData[idx]; },
  338.   getCellValue: function(idx, column){ return gTreeData[idx].checked; },
  339.   isContainerOpen: function(idx)     { return gTreeData[idx].open; },
  340.   isContainerEmpty: function(idx)    { return false; },
  341.   isSeparator: function(idx)         { return false; },
  342.   isSorted: function()               { return false; },
  343.   isEditable: function(idx, column)  { return false; },
  344.   getLevel: function(idx)            { return this.isContainer(idx) ? 0 : 1; },
  345.  
  346.   getParentIndex: function(idx) {
  347.     if (!this.isContainer(idx))
  348.       for (var t = idx - 1; t >= 0 ; t--)
  349.         if (this.isContainer(t))
  350.           return t;
  351.     return -1;
  352.   },
  353.  
  354.   hasNextSibling: function(idx, after) {
  355.     var thisLevel = this.getLevel(idx);
  356.     for (var t = after + 1; t < gTreeData.length; t++)
  357.     if (this.getLevel(t) <= thisLevel)
  358.       return this.getLevel(t) == thisLevel;
  359.     return false;
  360.   },
  361.  
  362.   toggleOpenState: function(idx) {
  363.     if (!this.isContainer(idx))
  364.       return;
  365.     var item = gTreeData[idx];
  366.     if (item.open) {
  367.       // remove this window's tab rows from the view
  368.       var thisLevel = this.getLevel(idx);
  369.       for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
  370.       var deletecount = t - idx - 1;
  371.       gTreeData.splice(idx + 1, deletecount);
  372.       this.treeBox.rowCountChanged(idx + 1, -deletecount);
  373.     }
  374.     else {
  375.       // add this window's tab rows to the view
  376.       var toinsert = gTreeData[idx].tabs;
  377.       for (var i = 0; i < toinsert.length; i++)
  378.         gTreeData.splice(idx + i + 1, 0, toinsert[i]);
  379.       this.treeBox.rowCountChanged(idx + 1, toinsert.length);
  380.     }
  381.     item.open = !item.open;
  382.     this.treeBox.invalidateRow(idx);
  383.   },
  384.  
  385.   getCellProperties: function(idx, column, prop) {
  386.     if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
  387.       prop.AppendElement(this._getAtom("partial"));
  388.     if (column.id == "title")
  389.       prop.AppendElement(this._getAtom(this.getImageSrc(idx, column) ? "icon" : "noicon"));
  390.   },
  391.  
  392.   getRowProperties: function(idx, prop) {},
  393.  
  394.   getImageSrc: function(idx, column) {
  395.     if (column.id == "title")
  396.       return gTreeData[idx].src || null;
  397.     return null;
  398.   },
  399.   
  400.   initialize: function() {
  401.     var count;
  402.     if (gTreeData) count = this.rowCount;
  403.     gTreeData = [];
  404.     if (this.treeBox && count)
  405.       this.treeBox.rowCountChanged(0, -count);
  406.   },
  407.  
  408.   getProgressMode : function(idx, column) { },
  409.   cycleHeader: function(column) { },
  410.   cycleCell: function(idx, column) { },
  411.   selectionChanged: function() { },
  412.   performAction: function(action) { },
  413.   performActionOnCell: function(action, index, column) { },
  414.   getColumnProperties: function(column, prop) { }
  415. };
  416.